package justweb

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	lru "github.com/hashicorp/golang-lru"
)

// 文件缓存结构
type fileCacheItem struct {
	fileName    string
	fileSize    int
	contentType string
	data        []byte
}

// 静态资源服务
type StaticResourceHandler struct {
	dir                     string // 文件夹位置
	pathPrefix              string // 路由的前缀
	extensionContentTypeMap map[string]string

	// 缓存
	cache       *lru.Cache
	maxFileSize int // 大文件
}

type StaticOptions func(sr *StaticResourceHandler)

func NewStaticHandler(dir string, pathPrefix string, sop ...StaticOptions) *StaticResourceHandler {
	res := &StaticResourceHandler{
		dir:        dir,
		pathPrefix: pathPrefix,
		extensionContentTypeMap: map[string]string{
			// 文件的类型
			"jpeg": "image/jpeg",
			"jpe":  "image/jpeg",
			"jpg":  "image/jpeg",
			"png":  "image/png",
			"pdf":  "image/pdf",
		},
	}

	// 迭代选项的方法
	for _, o := range sop {
		o(res)
	}

	return res
}

// 静态资源服务
func (h *StaticResourceHandler) ServeStaticResource(c *Context) {
	req := strings.TrimPrefix(c.R.URL.Path, h.pathPrefix)

	// 查找缓存
	if item, ok := h.readCacheData(req); ok {
		fmt.Printf("read data from cache... %s\n", item.fileName)
		h.writeItemAsResponse(item, c.W)
		return
	}

	// 打开文件
	path := filepath.Join(h.dir, req)
	f, err := os.Open(path)
	if err != nil {
		c.W.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer f.Close() // 关闭

	// 判断文件
	ext := getFileExt(f.Name())
	t, ok := h.extensionContentTypeMap[ext]
	if !ok {
		c.W.WriteHeader(http.StatusBadRequest)
		return
	}

	// 写入返回
	data, err := ioutil.ReadAll(f)
	if err != nil {
		c.W.WriteHeader(http.StatusInternalServerError)
		return
	}

	// c.W.WriteHeader(http.StatusOK)
	// c.W.Header().Set("Content-Type", t)
	// c.W.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
	// _, _ = c.W.Write(data)

	item := &fileCacheItem{
		fileSize:    len(data),
		data:        data,
		contentType: t,
		fileName:    req,
	}
	h.writeItemAsResponse(item, c.W)

	// 缓存
	h.cacheFile(item)

}

// 获取后缀
func getFileExt(name string) string {
	index := strings.LastIndex(name, ".")
	if index == len(name)-1 {
		return ""
	}
	return name[index+1:]
}

// 建立缓存层
func WithFileCache(maxFileSizeThreshold int, maxCacheFileCnt int) StaticOptions {
	return func(h *StaticResourceHandler) {
		c, err := lru.New(maxCacheFileCnt) // 最大缓存数量
		if err != nil {
			fmt.Printf("could not create LRU, we won't cache static file")
		}
		h.maxFileSize = maxFileSizeThreshold // 超过这个大文件大小，不缓存
		h.cache = c
	}
}

// 扩展文件类型
func WithMoreExtension(extMap map[string]string) StaticOptions {
	return func(h *StaticResourceHandler) {
		for ext, contentType := range extMap {
			h.extensionContentTypeMap[ext] = contentType
		}
	}
}

// 写入缓存
func (h *StaticResourceHandler) cacheFile(item *fileCacheItem) {
	if h.cache != nil && item.fileSize < h.maxFileSize {
		h.cache.Add(item.fileName, item)
	}
}

// 写入响应
func (h *StaticResourceHandler) writeItemAsResponse(item *fileCacheItem, writer http.ResponseWriter) {
	writer.WriteHeader(http.StatusOK)
	writer.Header().Set("Content-Type", item.contentType)
	writer.Header().Set("Content-Length", fmt.Sprintf("%d", item.fileSize))
	_, _ = writer.Write(item.data)

}

// 查找缓存
func (h *StaticResourceHandler) readCacheData(fileName string) (*fileCacheItem, bool) {
	if h.cache != nil {
		if item, ok := h.cache.Get(fileName); ok {
			return item.(*fileCacheItem), true
		}
	}
	return nil, false
}
